#ifndef SIMPLE_C_CODE_GENERATOR_H
#define SIMPLE_C_CODE_GENERATOR_H

#include "SyntaxVisitors.h"
#include "BinaryCode.h"

namespace SimpleC
{
	namespace Compiler
	{
		class VariableOffset
		{
		public:
			int Offset;
			ExpressionType Type;
			VariableOffset()
			{}
			VariableOffset(VariableOffset && var)
			{
				Offset = var.Offset;
				Type = var.Type;
			}
			VariableOffset & operator = (VariableOffset && var)
			{
				Offset = var.Offset;
				Type = var.Type;
				return *this;
			}
		};


		class CodeGenerator : public SyntaxVisitor
		{
		private:
			int paramSize;
			int localVarSize;
			FunctionCode * currentFunctionCode;
			Dictionary<String, VariableOffset> currentVariables;
		private:
			void EmitInstruction(OpCode opCode, int param);
			void EmitInstruction(OpCode opCode, int param1, int param2);
			void EmitInstruction(OpCode opCode, double param);
			void EmitInstruction(OpCode opCode);
			void GenerateAssignment(ExpressionSyntaxNode * lvalueExpr);
			int GetCurrentInstructionPos()
			{
				return currentFunctionCode->Instructions.Count();
			}
		private:
			List<int> labelPosMap;
			List<int> loopLabels;
			int GetCurrentLoopBeginLabel()
			{
				return loopLabels.Last();
			}
			int GetCurrentLoopBreakLabel()
			{
				return loopLabels.Last() + 2;
			}
			int GetCurrentLoopContinueLabel()
			{
				return loopLabels.Last() + 1;
			}
			void SetCurrentLoopBeginPosition(int pos)
			{
				labelPosMap[loopLabels.Last()] = pos;
			}
			void SetCurrentLoopBreakPosition(int pos)
			{
				labelPosMap[loopLabels.Last() + 2] = pos;
			}
			void SetCurrentLoopContinuePosition(int pos)
			{
				labelPosMap[loopLabels.Last() + 1] = pos;
			}
			void PushLoopLabels()
			{
				loopLabels.Add(labelPosMap.Count());
				labelPosMap.Add(-1);
				labelPosMap.Add(-1);
				labelPosMap.Add(-1);
			}
			void PopLoopLabels()
			{
				loopLabels.RemoveAt(loopLabels.Count() - 1);
			}
		private:
			ProgramSyntaxNode * programSyntaxNode;
			void GenerateLValueOffset(ExpressionSyntaxNode * expr);
			int GetVarOffset(const String & varName)
			{
				return currentVariables[varName].GetValue().Offset;
			}
		public:
			RefPtr<ProgramCode> ProgramBinaryCode;

			virtual void VisitProgram(ProgramSyntaxNode * program);
			
			virtual void VisitFunction(FunctionSyntaxNode* function);
			virtual void VisitBlockStatement(BlockStatementSyntaxNode* stmt);
			virtual void VisitBreakStatement(BreakStatementSyntaxNode* stmt);
			virtual void VisitContinueStatement(ContinueStatementSyntaxNode* stmt);
			virtual void VisitDoWhileStatement(DoWhileStatementSyntaxNode* stmt);
			virtual void VisitEmptyStatement(EmptyStatementSyntaxNode* stmt);
			virtual void VisitForStatement(ForStatementSyntaxNode* stmt);
			virtual void VisitIfStatement(IfStatementSyntaxNode* stmt);
			virtual void VisitReturnStatement(ReturnStatementSyntaxNode* stmt);
			virtual void VisitVarDeclrStatement(VarDeclrStatementSyntaxNode* stmt);
			virtual void VisitWhileStatement(WhileStatementSyntaxNode* stmt);
			virtual void VisitExpressionStatement(ExpressionStatementSyntaxNode* stmt);

			virtual void VisitBinaryExpression(BinaryExpressionSyntaxNode* expr);
			virtual void VisitConstantExpression(ConstantExpressionSyntaxNode* expr);
			virtual void VisitIndexExpression(IndexExpressionSyntaxNode* expr);
			virtual void VisitInvokeExpression(InvokeExpressionSyntaxNode* expr);
			virtual void VisitUnaryExpression(UnaryExpressionSyntaxNode* expr);
			virtual void VisitVarExpression(VarExpressionSyntaxNode* expr);
			virtual void VisitParameter(ParameterSyntaxNode* para);
			virtual void VisitType(TypeSyntaxNode* type);
			virtual void VisitDeclrVariable(VarDeclrStatementSyntaxNode::Variable* variable);
		};
	}
}

#endif